package org.metasketch.editor.links;

import groovy.lang.Binding;
import groovy.lang.GroovyShell;
import javafx.scene.Node;
import javafx.scene.shape.Line;

import org.metasketch.diagram.javafx.graphics.BaseGraphic;
import org.metasketch.diagram.javafx.graphics.BaseLink;
import org.metasketch.editor.timeline.SnapshotManager;
import org.metasketch.graphics.GraphicLink;
import org.metasketch.graphics.Point;
import org.metasketch.graphics.registry.IGraphic;
import org.metasketch.javafx.graphics.JFXDiagram;
import org.metasketch.model.BaseModel;
import org.metasketch.model.CommonIdentified;
import org.metasketch.reflection.RefUtil;

public class LinkBuilder {

	public static void setupLink(ILink link, String graphicTypeName,
			Class graphicalType, EndType initial, BaseGraphic initialGraphic,
			EndType finalType, BaseGraphic finalGraphic, Point startPoint,
			Point endPoint, JFXDiagram jfxDiagram, BaseModel model,
			CommonIdentified semantic, boolean takeSnapshot) throws Throwable {

		BaseGraphic source, target = null;
		if (initial == EndType.SOURCE) {
			source = initialGraphic;
			target = finalGraphic;
		} else {
			source = finalGraphic;
			target = initialGraphic;
		}

		// if (source == null) {
		// Point startPoint = new Point(startX, startY);
		// } else {
		//
		// }

		// if (target == null) {
		// Point endPoint = new Point(endX, endY);
		// } else {
		//
		// }

		setupLink(link, graphicTypeName, graphicalType, source, target,
				jfxDiagram, model, startPoint, endPoint, semantic, takeSnapshot);

	}

	public static void setupLink(ILink link, String graphicTypeName,
			Class graphicalType, BaseGraphic source, BaseGraphic target,
			JFXDiagram jfxDiagram, BaseModel model, Point start, Point end,
			CommonIdentified semantic, boolean takeSnapshot) throws Throwable {

		RefUtil util = new RefUtil();

		// can't create a link between the same object
		if ((source != null && target != null)
				&& (source == target
						&& source.getSemantic() == target.getSemantic() && source
						.getGraphicNode().getType()
						.equals(target.getGraphicNode().getType())))
			return;

		// if link distance smaller than 10, then don't create the link
		if (start != null && end != null && start.withinRadius(10.0, end))
			return;

		// CommonIdentified semantic = null;

		if (source != null && target != null) {

			// Setup the semantic stuff from the link...
			CommonIdentified sourceObject = source.getSemantic();
			CommonIdentified targetObject = target.getSemantic();

			// setting up the fields on the source and targets if there are
			// any...
			if (sourceObject != null && targetObject != null) {

				ILinkConfig linkConfig = link.getLinkConfig(source, target);

				// else {

				BaseEndConfig srcEnd = linkConfig.getSource();
				BaseEndConfig trgEnd = linkConfig.getTarget();

				if (srcEnd != null && srcEnd.getFieldName() != null) {

					if (srcEnd.getSemanticFieldName() == null)
						util.setValue(sourceObject, srcEnd.getFieldName(),
								targetObject);
					else {
						String field = srcEnd.getSemanticFieldName();
						if (field.contains("semantic."))
							field = field.replace("semantic.", "");
						Object value = util.getValue(targetObject, field);
						util.setValue(sourceObject, srcEnd.getFieldName(),
								value);
					}

				}

				if (trgEnd != null && trgEnd.getFieldName() != null) {
					if (trgEnd.getSemanticFieldName() == null)
						util.setValue(targetObject, trgEnd.getFieldName(),
								sourceObject);
					else {
						String field = trgEnd.getSemanticFieldName();
						if (field.contains("semantic."))
							field = field.replace("semantic.", "");
						Object value = util.getValue(sourceObject, field);
						util.setValue(targetObject, trgEnd.getFieldName(),
								value);
					}
				}

				if (linkConfig.getScript() != null) {

					Binding binding = new Binding();
					// binding.setVariable("semantic", semantic);

					// binding.setVariable("semantic", semantic);
					// binding.setVariable("graphic", semantic);

					binding.setVariable("sourceSemantic", sourceObject);
					binding.setVariable("targetSemantic", targetObject);

					binding.setVariable("sourceGraphic", source);
					binding.setVariable("targetGraphic", target);

					GroovyShell shell = new GroovyShell(binding);

					shell.evaluate(linkConfig.getScript());

				}

				// }
			}

			// Setup the relationship object and its references to source and
			// target objects, as well as source and target object references to
			// the relationship object.

			if (semantic == null && link.getSemanticConfig() != null) {

				SemanticConfig cfg = link.getSemanticConfig();

				// if (cfg.getSemanticType() != null) {

				semantic = cfg.createNewInstance();

				if (semantic != null) {
					model.addChild(semantic);

					// else {

					// setup source object references to link semantic
					// object
					if (cfg.getSourceFieldname() != null) {

						if (cfg.getSemanticSourceFieldname() == null)
							util.setValue(sourceObject,
									cfg.getSourceFieldname(), semantic);
						else {
							String field = cfg.getSemanticSourceFieldname();
							if (field.contains("semantic."))
								field = field.replace("semantic.", "");
							Object value = util.getValue(semantic, field);
							util.setValue(sourceObject,
									cfg.getSourceFieldname(), value);
						}

					}

					// setup target object references to link semantic
					// object
					if (cfg.getTargetFieldname() != null) {

						if (cfg.getSemanticTargetFieldname() == null)
							util.setValue(targetObject,
									cfg.getSourceFieldname(), semantic);
						else {
							String field = cfg.getSemanticTargetFieldname();
							if (field.contains("semantic."))
								field = field.replace("semantic.", "");
							Object value = util.getValue(semantic, field);
							util.setValue(targetObject,
									cfg.getTargetFieldname(), value);
						}

					}

					// setup link semantic object references to source
					// object
					if (cfg.getLinkSourceFieldname() != null) {

						if (cfg.getLinkSemanticSourceFieldname() == null)
							util.setValue(semantic,
									cfg.getLinkSourceFieldname(), sourceObject);
						else {
							String field = cfg.getLinkSemanticSourceFieldname();
							if (field.contains("semantic."))
								field = field.replace("semantic.", "");
							Object value = util.getValue(sourceObject, field);
							util.setValue(semantic,
									cfg.getLinkSourceFieldname(), value);
						}

					}

					// setup link semantic object references to target
					// object
					if (cfg.getLinkTargetFieldname() != null) {

						if (cfg.getLinkSemanticTargetFieldname() == null)
							util.setValue(semantic,
									cfg.getLinkTargetFieldname(), targetObject);
						else {
							String field = cfg.getLinkSemanticTargetFieldname();
							if (field.contains("semantic."))
								field = field.replace("semantic.", "");
							Object value = util.getValue(targetObject, field);
							util.setValue(semantic,
									cfg.getLinkTargetFieldname(), value);
						}

					}

					if (cfg.getSemanticSetupScript() != null) {

						Binding binding = new Binding();

						binding.setVariable("semantic", semantic);
						// TODO add graphic ????

						binding.setVariable("sourceSemantic", sourceObject);
						binding.setVariable("targetSemantic", targetObject);

						binding.setVariable("sourceGraphic", source);
						binding.setVariable("targetGraphic", target);

						GroovyShell shell = new GroovyShell(binding);

						shell.evaluate(cfg.getSemanticSetupScript());

					}

					// }
				}
				// }

			}

		}

		// TODO setup field in source and target elements
		// source.getSemantic();
		// target.getSemantic();

		IGraphic node = (IGraphic) graphicalType.newInstance();
		BaseLink baseLink = (BaseLink) node;

		GraphicLink graphicLink = (GraphicLink) node.getGraphicNode();
		graphicLink.setType(graphicTypeName);
		if (semantic != null) {

			graphicLink.setSemantic(semantic.getId());
			// model.addChild(semantic);
		}

		if (source != null)
			graphicLink.setSource(source.getId());
		if (target != null)
			graphicLink.setTarget(target.getId());

		if (source != null) {
			Point point = source.getCompleteCoordinatesThroughParent();
			point.setX(point.getX() + source.getWidth() / 2);
			point.setY(point.getY() + source.getHeight() / 2);
			graphicLink.getPoints().add(point);
		} else
			graphicLink.getPoints().add(new Point(start.getX(), start.getY()));

		if (target != null) {
			Point point = target.getCompleteCoordinatesThroughParent();
			point.setX(point.getX() + source.getWidth() / 2);
			point.setY(point.getY() + source.getHeight() / 2);
			graphicLink.getPoints().add(point);
		} else
			graphicLink.getPoints().add(new Point(end.getX(), end.getY()));

		baseLink.setLink(link);
		baseLink.setSource(source);
		baseLink.setTarget(target);
		baseLink.setup(semantic, jfxDiagram.getDiagram(), model);

		jfxDiagram.getDiagram().getGraphics().add(baseLink.getGraphicNode());
		jfxDiagram.addNode((Node) baseLink);

		// alert models something has changed !!!
		// if (source != null) {
		// //if there is no semantic
		// // if (semantic == null)
		// // source.addModelChangeListener(baseLink);
		// source.alertModelChanged();
		// }
		//
		// if (target != null) {
		// // if (semantic == null)
		// // target.addModelChangeListener(baseLink);
		// target.alertModelChanged();
		// }

		model.getModelChangeManager().alertChanges();

		if (takeSnapshot)
			SnapshotManager.takeSnapshot();

	}

}
